package com.hdd.art.utils.http;


import com.hdd.art.utils.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okhttp3.Request.Builder;
import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;


/**
 * 工具类(基于OKHttp3)用来发送网络请求
 */
@Slf4j
public final class HttpClient {

    /** 发送网络请求的客户端，线程安全  */
    private static final OkHttpClient CLIENT;

    static {
        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
        // 连接失败则重试
        clientBuilder.retryOnConnectionFailure(true);
        // 连接超时时间限制为30秒
        clientBuilder.connectTimeout(30, TimeUnit.SECONDS);
        // 读取的超时限制为30秒
        clientBuilder.readTimeout(30, TimeUnit.SECONDS);
        // 写入的超时限制为30秒
        clientBuilder.writeTimeout(30, TimeUnit.SECONDS);
        // 构建全局唯一client对象
        CLIENT = clientBuilder
//                .sslSocketFactory(createSSLSocketFactory())
//                .hostnameVerifier(new TrustAllHostnameVerifier())
                .build();
    }

    /**
     * 向指定url发送get请求
     * @param url 请求的url
     * @return 响应结果
     */
    public static OkHttpResponse<String> doGet(String url) {
        return doGet(url, null);
    }

    /**
     * 发送同步get请求
     * @param url 请求的网络url
     * @param headers 请求头信息
     * @return 响应结果
     */
    public static OkHttpResponse<String> doGet(String url, Map<String, String> headers) {
        Builder builder = new Builder().url(url);
        // 增加请求头
        addHeaders(builder, headers);
        // 构建request对象
        Request request = builder.build();
        // 用client发送请求
        Call call = CLIENT.newCall(request);
        OkHttpResponse<String> response = new OkHttpResponse<>();
        try {
            Response result = call.execute();
            response.setData(result.body().string());
            setExtData(result, response);
        } catch (Exception e) {
            response.setMessage(e.getMessage());
            log.error("向{}发送请求发生异常", url, e);
        }
        return response;
    }

    /**
     * 发送<font color=red>异步</font>get请求,不在乎结果
     * @param url 请求的网络url
     */
    public static void asyncGet(String url) {
        asyncGet(url, null);
    }

    /**
     * 发送<font color=red>异步</font>get请求
     * @param url 请求的网络url
     * @param callBack 回调
     */
    public static void asyncGet(String url, Callback callBack) {
        asyncGet(url, null, callBack);
    }

    /**
     * 发送<font color=red>异步</font>get请求
     * @param url 请求的网络url
     * @param headers 请求头信息
     * @param callBack 回调
     */
    public static void asyncGet(String url, Map<String, String> headers, Callback callBack) {
        Builder builder = new Builder().url(url);
        // 增加请求头
        addHeaders(builder, headers);
        // 构建request对象
        Request request = builder.build();
        // 用client发送请求
        Call call = CLIENT.newCall(request);
        // 检查是否需要回调
        if (callBack == null) {
            callBack = new Callback() {

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    // do nothing
                }

                @Override
                public void onFailure(Call call, IOException e) {
                    // do nothing
                }
            };
        }
        // 发送异步请求
        call.enqueue(callBack);
    }

    /**
     * xml格式post请求接口调用
     * @param url   接口地址
     * @param xmlStr   xml格式请求参数体
     * @return
     */
    public static String postXml(String url,String xmlStr){
        RequestBody body=RequestBody.create(MediaType.parse("application/xml"),xmlStr);
        Request requestOk = new Builder()
                .url(url)
                .post(body)
                .build();

        Response response;
        try {
            response = new OkHttpClient().newCall(requestOk).execute();
            String jsonString = response.body().string();
            if(response.isSuccessful()){
                return jsonString;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return "";
    }

    /**
     * 向指定url发送同步post请求,请求参数为K-V形式
     * @param url 请求的url
     * @param params 请求参数map
     * @return 响应结果
     */
    public static OkHttpResponse<String> doPost(String url, Map<String, String> params) {
        return doPost(url, params, null);
    }

    /**
     * 向指定url发送<font color=red>同步</font>post请求,请求参数为k-v形式
     * @param url 请求的url
     * @param params 请求参数map
     * @param headers 请求头信息
     * @return 响应结果
     */
    public static OkHttpResponse<String> doPost(String url, Map<String, String> params, Map<String, String> headers) {
        // 添加请求头
        Builder requestBuilder = new Builder().url(url);
        addHeaders(requestBuilder, headers);
        // 构建表单并在表单中添加参数
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        if (params != null && params.size() > 0) {
            for (Entry<String, String> en : params.entrySet()) {
                formBodyBuilder.add(en.getKey(), en.getValue());
            }
        }
        // 构建表单对象
        FormBody formBody = formBodyBuilder.build();
        Request request = requestBuilder.post(formBody).build();
        // 发送请求
        Call call = CLIENT.newCall(request);
        OkHttpResponse<String> response = new OkHttpResponse<>();
        try {
            Response result = call.execute();
            response.setData(result.body().string());
            setExtData(result, response);
        } catch (Exception e) {
            response.setMessage(e.getMessage());
            log.error("向{}发送post请求发生异常,入参:[{}]", url, JsonUtil.toJsonString(params), e);


        }
        return response;
    }

    /**
     * 发送异步post请求，不在乎结果
     * @param url 请求的url
     * @param params 请求参数
     */
    public static void asyncPost(String url, Map<String, String> params) {
        asyncPost(url, params, null);
    }

    /**
     * 向指定url发送<font color=red>异步</font>post请求
     * @param url 请求的url
     * @param params 请求参数map
     * @param callBack 回调
     */
    public static void asyncPost(String url, Map<String, String> params, Callback callBack) {
        asyncPost(url, params, null, callBack);
    }

    /**
     * 向指定url发送<font color=red>异步</font>post请求
     * @param url 请求的url
     * @param params 请求参数map
     * @param headers 请求头信息
     * @param callBack 回调
     */
    public static void asyncPost(String url, Map<String, String> params, Map<String, String> headers, Callback callBack) {
        // 添加请求头
        Builder requestBuilder = new Builder().url(url);
        addHeaders(requestBuilder, headers);
        // 构建表单并在表单中添加参数
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        if (params != null && params.size() > 0) {
            for (Entry<String, String> en : params.entrySet()) {
                formBodyBuilder.add(en.getKey(), en.getValue());
            }
        }
        // 构建表单对象
        FormBody formBody = formBodyBuilder.build();
        Request request = requestBuilder.post(formBody).build();
        // 发送请求
        Call call = CLIENT.newCall(request);
        if (callBack == null) {
            callBack = new Callback() {
                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    // do nothing
                }

                @Override
                public void onFailure(Call call, IOException e) {
                    // do nothing
                }
            };
        }
        call.enqueue(callBack);
    }

    /**
     * 向指定url发送post请求,请求参数为json形式
     * @param url 请求的url
     * @param paramJson 请求参数，json字符串
     */
    public static OkHttpResponse<String> postJson(String url, String paramJson) {
        return postJson(url, paramJson, null);
    }

    /**
     * 向指定url发送post请求,请求参数为json形式
     * @param url 请求的url
     * @param paramJson 请求参数，json字符串
     */

    public static OkHttpResponse<String> postJson(String url, String paramJson, Map<String, String> headers) {
        MediaType json = MediaType.parse("application/json; charset=utf-8");
        RequestBody requestBody = RequestBody.create(json, paramJson);
        Builder requestBuilder = new Builder().url(url);
        addHeaders(requestBuilder, headers);
        Request request = requestBuilder.post(requestBody).build();
        // 发送请求
        Call call = CLIENT.newCall(request);
        OkHttpResponse<String> response = new OkHttpResponse<>();
        try {
            Response result = call.execute();
            response.setData(result.body().string());
            setExtData(result, response);
        } catch (IOException e) {
            response.setMessage(e.getMessage());
            log.error("向{}发送post请求发生异常,请求参数为[{}]", url, paramJson, e);
        }
        return response;
    }

    private static class TrustAllCerts implements X509TrustManager {
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}

        public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}
    }
    private static class TrustAllHostnameVerifier implements HostnameVerifier {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }
    private static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;

        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null,  new TrustManager[] { new TrustAllCerts() }, new SecureRandom());

            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
        }

        return ssfFactory;
    }


    /**
     * 上传文件
     * @param url 请求的url
     * @param files 要上传的一个或多个文件
     */
    public static OkHttpResponse<String> uploadFile(String url, File... files) {
        return uploadFile(url, null, files);
    }

    /**
     * 上传文件
     * @param url 请求的url
     * @param params 请求参数
     * @param files 要上传的一个或多个文件
     */
    public static OkHttpResponse<String> uploadFile(String url, Map<String, String> params, File... files) {
        return uploadFile(url, params, null, files);
    }

    /**
     * 上传文件
     * @param url 请求的url
     * @param params 请求参数
     * @param headers 请求头信息
     * @param files 要上传的一个或多个文件
     */
    public static OkHttpResponse<String> uploadFile(String url, Map<String, String> params, Map<String, String> headers, File ...files) {
        // 添加请求头
        Builder requestBuilder = new Builder().url(url);
        addHeaders(requestBuilder, headers);
        // 构建请求
        MultipartBody.Builder formBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
        // 塞入表单参数
        if (params != null && params.size() > 0) {
            for (Entry<String, String> en : params.entrySet()) {
                formBodyBuilder.addFormDataPart(en.getKey(), en.getValue());
            }
        }
        // 塞入文件
        if (files != null && files.length > 0) {
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                RequestBody body = RequestBody.create(MediaType.parse("application/octet-stream"), file);
                formBodyBuilder.addFormDataPart("file" + (i + 1), file.getName(), body);
            }
        }
        RequestBody requestBody = formBodyBuilder.build();
        // 构建请求
        Request request = requestBuilder.post(requestBody).build();
        // 发送请求
        Call call = CLIENT.newCall(request);
        OkHttpResponse<String> response = new OkHttpResponse<>();
        try {
            Response result = call.execute();
            response.setData(result.body().string());
            setExtData(result, response);
        } catch (Exception e) {
            response.setMessage(e.getMessage());
            log.error("向{}上传文件发生异常,请求参数为[{}]", url, JsonUtil.toJsonString(params), e);
        }
        return response;
    }

    /**
     * 下载文件(以返回流的形式传输)
     * @param url 请求的url
     * @return inputstream
     */
    public static OkHttpResponse<InputStream> downloadFile(String url) {
        return downloadFile(url, null);
    }

    /**
     * 下载文件(以返回流的形式传输)
     * @param url 请求的url
     * @param params 请求参数
     * @return inputstream
     */
    public static OkHttpResponse<InputStream> downloadFile(String url, Map<String, String> params) {
        return downloadFile(url, params, null);
    }

    /**
     * 下载文件(以返回流的形式传输)
     * @param url 请求的url
     * @param params 请求参数
     * @param headers 请求头信息
     * @return inputstream
     */
    public static OkHttpResponse<InputStream> downloadFile(String url, Map<String, String> params, Map<String, String> headers) {
        // 添加请求头
        Builder requestBuilder = new Builder().url(url);
        addHeaders(requestBuilder, headers);
        // 构建表单并在表单中添加参数
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        // 塞入请求参数
        if (params != null && params.size() > 0) {
            for (Entry<String, String> en : params.entrySet()) {
                formBodyBuilder.add(en.getKey(), en.getValue());
            }
        }
        // 构建请求
        RequestBody requestBody = formBodyBuilder.build();
        Request request = requestBuilder.post(requestBody).build();
        // 发送请求
        Call call = CLIENT.newCall(request);
        OkHttpResponse<InputStream> response = new OkHttpResponse<>();
        try {
            Response result = call.execute();
            response.setData(result.body().byteStream());
            setExtData(result, response);
        } catch (Exception e) {
            response.setMessage(e.getMessage());
            log.error("向{}下载文件发生异常,请求参数为[{}]", url, JsonUtil.toJsonString(params), e);
        }
        return response;
    }

    /**
     * 填充一些信息，除了数据
     */
    private static void setExtData(Response result, OkHttpResponse<?> response) {
        if (result.priorResponse() != null) {
            response.setRedirect(result.priorResponse().isRedirect());
            response.setCode(result.priorResponse().code());
        } else {
            response.setRedirect(result.isRedirect());
            response.setCode(result.code());
        }
        response.setSuccess(result.isSuccessful());
        response.setMessage(result.message());
        response.setUrl(result.request().url().toString());
    }

    /**
     * 给指定请求，增加请求头
     */
    private static void addHeaders(Builder requestBuilder, Map<String, String> headers) {
        if (headers != null && headers.size() > 0) {
            for (Entry<String, String> en : headers.entrySet()) {
                requestBuilder.header(en.getKey(), en.getValue());
            }
        }
    }

    /** 私有构造方法，禁止工具类实例化 */
    private HttpClient() {

    }

}